/*
 * Code to controll an 8x8x8 ledcube using avr
 * http://www.instructables.com/id/Led-Cube-8x8x8/
 * See lisence.txt for lisence.
 */
#include "main.h"
#include "effect.h"
#include "launch_effect.h"
#include "draw.h"

// Main loop
// the AVR enters this function at boot time
int main (void)
{

	// This function initiates IO ports, timers, interrupts and
    // serial communications
	ioinit();
	
    // This variable specifies which layer is currently being drawn by the
	// cube interrupt routine. We assign a value to it to make sure it's not >7.
	current_layer = 1;	

	int i;
	
	// Boot wait
	// This function serves 3 purposes
	// 1) We delay starting up any interrupts, as drawing the cube causes a lot
	//    noise that can confuse the ISP programmer.
	// 2) Listen for button press. One button means go into rs232 mode,
	//    The other means go into autonomous mode and start doing stuff.
	// 3) Random seed. The bootwait function counts forever from 0 to 255.
	//    Whenever you press the button, this counter stops, and the number it
	//    stopped at is used as a random seed. This ensures true randomness at
	//    every boot. Without this (or some similar process) the cube would
	//    produce the same "random" sequence every time
	i = bootwait();

	// Enable interrupts
	// This starts the routine that draws the cube content
	sei();

	// Result for bootwait() is 2:
	// Go to rs232 mode. this function loops forever.
	if (i == 2)
	{
		rs232();
	}

	// Result of bootwait() is something other than 2:
	// Do awesome effects. Loop forever.
	while (1)
	{
		// Show the effects in a predefined order
		for (i=0; i<EFFECTS_TOTAL; i++)
			launch_effect(i);
		
		// Show the effects in a random order.
		// Comment the two lines above and uncomment this
		// if you want the effects in a random order.
		//launch_effect(rand()%EFFECTS_TOTAL);
	}

}

/*
 * Multiplexer/framebuffer routine
 * This function is called by an interrupt generated by timer 2.
 * Every time it runs, it does the following:
 * 1) Disable the output for the multiplexer array.
 * 2) Turn of all layers.
 * 3) Load the current layer from the cube buffer onto the
 *    multiplexer array.
 * 4) Enable output from the multiplexer array.
 * 5) Turn on the current layer.
 * 6) Increment the current_layer variable, so the next layer is
 *    drawn the next time this function runs.
*/

ISR(TIMER2_COMP_vect)
{
	int i;

	LAYER_SELECT = 0x00; // Turn all cathode layers off. (all transistors off)
	OE_PORT |= OE_MASK; // Set OE high, disabling all outputs on latch array

	// Loop through all 8 bytes of data in the current layer
	// and latch it onto the cube.
	for (i = 0; i < 8; i++)
	{
		// Set the data on the data-bus of the latch array.
		PORTA = cube[current_layer][i];
		// Increment the latch address chip, 74HC138, to create
		// a rising edge (LOW to HIGH) on the current latch.
		LATCH_ADDR = (LATCH_ADDR & LATCH_MASK_INV) | (LATCH_MASK & (i+1));
	}

	OE_PORT &= ~OE_MASK; // Set OE low, enabling outputs on the latch array
	LAYER_SELECT = (0x01 << current_layer); // Transistor ON for current layer

	// Increment the curren_layer counter so that the next layer is
	// drawn the next time this function runs.
	current_layer++;
	// We want to count from 0-7, so set it to 0 when it reaches 8.
	if (current_layer == 8)
		current_layer = 0;
}


void ioinit (void)
{
	DDRA = 0xff;	// DATA bus output
	DDRB = 0xef;	// Button on B4
	DDRC = 0xff;	// Layer select output
	DDRD = 0xdf;	// Button on D5
	
	
	PORTA = 0x00; // Set data bus off
	PORTC = 0x00; // Set layer select off
	PORTB = 0x10; // Enable pull up on button.
	PORTD = 0x20; // Enable pull up on button.


	// Timer 2
	// Frame buffer interrupt
	// 14745600/128/11 = 10472.72 interrupts per second
	// 10472.72/8 = 1309 frames per second
	OCR2 = 10; 	// interrupt at counter = 10
    TCCR2 |= (1 << CS20) | (1 << CS22); // Prescaler = 128.
	TCCR2 |= (1 << WGM21); // CTC mode. Reset counter when OCR2 is reached.
	TCNT2 = 0x00;	// initial counter value = 0;
	TIMSK |= (1 << OCIE2); // Enable CTC interrupt



    // Initiate RS232
    // USART Baud rate is defined in MYUBRR
    UBRRH = MYUBRR >> 8;
    UBRRL = MYUBRR;
    // UCSRC - USART control register
    // bit 7-6      sync/ascyn 00 = async,  01 = sync
    // bit 5-4      parity 00 = disabled
    // bit 3        stop bits 0 = 1 bit  1 = 2 bits
    // bit 2-1      frame length 11 = 8
    // bit 0        clock polarity = 0
    UCSRC  = 0b10000110;
    // Enable RS232, tx and rx
    UCSRB = (1<<RXEN)|(1<<TXEN);
    UDR = 0x00; // send an empty byte to indicate powerup.


}

// Boot wait function
// This function does 3 things:
// 1) Delay startup of interrupt. I've had some problems with in circuit
//    serial programming when the cube was running. I guess switching all
//    those LEDs on and off generates some noise.
// 2) Set a random random seed based on the delay between boot time and
//    the time you press a button.
// 3) Select mode of operation, autonomous or rs232 controlled.
unsigned int bootwait (void)
{
	// All the LED_PORT... code blinks the red and green status LEDs.

	unsigned int x = 0;
	LED_PORT |= LED_GREEN;
	while (1)
	{
        x++; // increment x by one.
		srand(x); // use counter x as random seed
		
		delay_ms(1000);
		LED_PORT &= ~LED_GREEN; // green off, red on
		LED_PORT |= LED_RED;
		
		// Listen for button presses and return with the
		// apropriate number.
		if (!(PIND & RS232_BTN))
			return 2;

		if (!(PINB & MAIN_BTN))
			return 1;
		
		delay_ms(1000);
		LED_PORT &= ~LED_RED; // red off, green on
		LED_PORT |= LED_GREEN;
		
		// Same as above. I do it twise because there are two delays
		// in this loop, used for the red and green led blinking..
		if (!(PIND & RS232_BTN))
			return 2;

		if (!(PINB & MAIN_BTN))
			return 1;
	}
}

// Take input from a computer and load it onto the cube buffer
void rs232(void)
{
	int tempval;
	int x = 0;
	int y = 0;
    int escape = 0;
	
	while (1)
	{
		// Switch state on red LED for debugging
		// Should switch state every time the code
		// is waiting for a byte to be received.
		LED_PORT ^= LED_RED;

		// Wait until a byte has been received
		while ( !(UCSRA & (1<<RXC)) );

		// Load the received byte from rs232 into a buffer.
		tempval = UDR;

		// Uncommet this to echo data back to the computer
		// for debugging purposes.
		//UDR = tempval;

		// Every time the cube receives a 0xff byte,
		// it goes into sync escape mode.
		// if a 0x00 byte is then received, the x and y counters
		// are reset to 0. This way the x and y counters are
		// always the same on the computer and in the cube.
		// To send an 0xff byte, you have to send it twice!

		// Go into sync escape mode
		if (tempval == 0xff)
		{
			// Wait for the next byte
			 while ( !(UCSRA & (1<<RXC)) );
			 // Get the next byte
			 tempval = UDR;

			 // Sync signal is received.
			 // Reset x and y counters to 0.
			 if (tempval == 0x00)
			 {
				x = 0;
				y = 0;
                escape = 1;
			 }
			 // if no 0x00 byte is received, proceed with
			 // the byte we just received.
		}

        if (escape == 0)
        {
		// Load data into the current position in the buffer
		fb[x][y] = tempval;

    		// Check if we have reached the limits of the buffer array.
    		if (y == 7)
    		{
    			if (x == 7)
    			{
    				// All data is loaded. Reset both counters
    				y = 0;
    				x = 0;
                    // Copy the data onto the cube.
    				tmp2cube();
    			} else
    			{
    				// A layer is loaded, reset y and increment x.
    				x++;
    				y = 0;
    			}
    		} else
    		{
    			// We are in the middle of loading a layer. increment y.
    			y++;
    		}
	
	    } else
        {
            escape = 0;
        }
    }
}


